package parser

import (
	"fmt"
	"os"
	"strings"
	"text/template"
	"unicode"

	"github.com/360EntSecGroup-Skylar/excelize"
)

var funcMap = template.FuncMap{}

func init() {

	funcMap["IsString"] = func(dataType string) bool {
		if dataType == "string" {
			return true
		}

		return false
	}

	funcMap["IsBool"] = func(dataType string) bool {
		if dataType == "bool" {
			return true
		}

		return false
	}

	funcMap["StrFirstToLower"] = func(str string) string {

		var upperStr string
		vv := []rune(str)
		for i := 0; i < len(vv); i++ {
			if i == 0 {
				if vv[i] >= 65 && vv[i] <= 90 {
					vv[i] += 32 // string的码表相差32位
					upperStr += string(vv[i])
				} else {
					fmt.Println("Not begins with upcase letter,")
					return str
				}
			} else {
				upperStr += string(vv[i])
			}
		}
		return upperStr
	}

	funcMap["NameTypeFunc"] = func(meta StructMeta) string {
		dataType := meta.DataType
		nameType := meta.NameType
		if nameType == "repeated" {
			if dataType == "int" || dataType == "int32" || dataType == "uint32" || dataType == "int64" || dataType == "uint64" || dataType == "string" {
				return "[]" + dataType
			} else if dataType == "bytes" {
				return "[]byte"
			} else {
				return "[]struct"
			}
		} else if nameType == "optional_struct" {
			return "struct"
		} else {
			if dataType == "int" || dataType == "uint" || dataType == "int32" || dataType == "uint32" || dataType == "int64" || dataType == "uint64" || dataType == "float32" || dataType == "float64" || dataType == "string" {
				return dataType
			} else if dataType == "String" {
				return "string"
			} else if dataType == "float" {
				return "float32"
			} else if dataType == "double" {
				return "float64"
			} else if dataType == "long" {
				return "int64"
			} else if dataType == "bytes" {
				return "[]byte"
			} else {
				return "struct"
			}
		}
	}

	// 客户端方法
	funcMap["NameTypeClientFunc"] = func(meta StructMeta) (ret string) {
		dataType := meta.DataType
		if dataType == "int" || dataType == "uint" || dataType == "int32" || dataType == "uint32" || dataType == "int64" || dataType == "uint64" || dataType == "float" || dataType == "long" || dataType == "string" {
			ret = dataType
		} else if dataType == "String" {
			ret = "string"
		} else if dataType == "bytes" {
			ret = "[]byte"
		} else {
			ret = "struct"
		}

		if strings.ToUpper(meta.Name) == "ID" { // 客户端ID 加入get set
			ret = fmt.Sprintf("public %s %s { get; set; }", ret, meta.Name)
		} else {
			ret = fmt.Sprintf("public %s %s;", ret, meta.Name)
		}

		return
	}

}

type StructMeta struct {
	Name           string      // 名称
	NameType       string      // required,optional,repeated
	DataType       string      // string, int32, float32
	Comment        string      // 注释
	IsAnonymStruct bool        // 是否是匿名结构
	AnonymStruct   *StructDesc // 匿名结构描述
}

type StructDesc struct { // 结构体描述
	Name        string // 结构体名字
	PrimaryKey  *StructMeta
	Field       []*StructMeta
	PropertyNum int
}

type StructPkg struct {
	PkgName         string        // 包名
	Name            string        // 结构名
	ClientNameSpace string        // 客户端命名空间
	ClientNick      string        // 客户端别名
	List            []*StructDesc // 各个描述
}

// 首字母大写
func Ucfirst(str string) string {
	for i, v := range str {
		return string(unicode.ToUpper(v)) + str[i+1:]
	}
	return ""
}

// 首字母小写
func Lcfirst(str string) string {
	for i, v := range str {
		return string(unicode.ToLower(v)) + str[i+1:]
	}
	return ""
}

// 结构体描述解析 对应一张表
func StructDescParse(metas []*DataMeta, name string) (desc *StructDesc) {
	desc = &StructDesc{
		Name:  name, // 结构体名字等于sheet的名字，sheet名字首字母要大写
		Field: []*StructMeta{},
	}

	for i := 0; i < len(metas); i++ {
		m := metas[i]
		if m == nil || m.Name == "" { // 没名字的 过滤掉
			continue
		}

		sm := &StructMeta{
			Name: Ucfirst(strings.TrimPrefix(m.Name, "_")),
			// NameType: m.NameType,
			DataType: m.DataType,
		}

		if m.Comment != "" {
			sm.Comment = strings.Replace(m.Comment, "\n", " ", -1)
		}
		if i == 2 { // 主键
			desc.PrimaryKey = sm
		}

		desc.Field = append(desc.Field, sm)
	}

	return
}

// 单表解析
func StructSheetParse(rows [][]string, sheet string) *StructDesc {

	columnNum := len(rows[0])

	// 元数据列表
	metaList := make([]*DataMeta, columnNum)

	// 前4行结构定义
	for j := 1; j < columnNum; j++ {

		if rows[3][j] == "END" {
			break
		}

		meta := &DataMeta{
			// NameType: rows[0][j],
			DataType: rows[2][j],
			Name:     rows[3][j],
			Comment:  rows[0][j],
		}

		metaList[j] = meta
	}

	// 结构体描述
	desc := StructDescParse(metaList, sheet)

	return desc
}

func StructParse(sheetSlice []string, xlsx *excelize.File) (ret string) {
	sdlist := []*StructDesc{}
	sdlistClient := []*StructDesc{}
	for _, sheet := range sheetSlice {
		rows := xlsx.GetRows(sheet)
		if len(rows) <= 0 {
			ret = fmt.Sprintf("[StructParse:sheet:%v] 表不存在或者为空 len(rows):%v", sheet, len(rows))
			return
		}
		desc := StructSheetParse(rows, clientPkg) //StructSheetParse(rows, sheet)
		sdlist = append(sdlist, desc)

		descClient := StructSheetParseClient(rows, clientPkg) //StructSheetParseClient(rows, sheet)
		sdlistClient = append(sdlistClient, descClient)

	}

	for _, name := range serverFiles {
		outFilename := fmt.Sprintf("%s\\%s.go", codeOutputPath+name, strings.ToLower(pkg))

		outFile, err := os.Create(outFilename)
		defer outFile.Close()
		if err != nil {
			return err.Error()
		}

		tmpl, err := template.New("struct_gen").Funcs(funcMap).Parse(tpl)
		if err != nil {
			return err.Error()
		}
		sdOnlyOnelist := []*StructDesc{}
		for _, d := range sdlist {
			sdOnlyOnelist = append(sdOnlyOnelist, d)
			break
		}
		err = tmpl.Execute(outFile, &StructPkg{PkgName: name, Name: strings.ToLower(pkg), List: sdOnlyOnelist})
		if err != nil {
			return err.Error()
		}
	}

	// 客户端代码
	var nick string
	if clientNick != "" {
		clientFiles = append(clientFiles, "Hotfix")
		clientFiles = append(clientFiles, "Main")
	}
	if clientNick == "clientHclientM" {
		clientNick = "AppType.ClientH | AppType.ClientM"
		nick = "AppType.ClientH | AppType.ClientM"
	} else if strings.Contains(clientNick, "clientH") {
		nick = "AppType.ClientH"
	} else if strings.Contains(clientNick, "clientM") {
		nick = "AppType.ClientM"
	}
	fmt.Println("clientNick:", clientNick)
	fmt.Println("nick:", nick)
	for _, name := range clientFiles {

		clientNameSpace := "ETModel"
		if name == "Hotfix" {
			clientNameSpace = "ETHotfix"
		}

		outFilename := fmt.Sprintf("%s\\%s.cs", clientCodeOutputPath+name, "T"+clientPkg+"AVO")

		outFile, err := os.Create(outFilename)
		defer outFile.Close()
		if err != nil {
			return err.Error()
		}

		tmpl, err := template.New("struct_gen").Funcs(funcMap).Parse(cstplH)
		if err != nil {
			return err.Error()
		}
		sdOnlyOnelist := []*StructDesc{}
		for _, d := range sdlistClient {
			sdOnlyOnelist = append(sdOnlyOnelist, d)
			break
		}
		err = tmpl.Execute(outFile, &StructPkg{Name: clientPkg, ClientNameSpace: clientNameSpace, ClientNick: nick, List: sdOnlyOnelist})
		if err != nil {
			return err.Error()
		}

	}

	return

}

func StructParseConfigValueAndSysLang(fileName string) string {

	var tpl string
	if fileName == "SystemLanguage" {
		tpl = `using ETModel;

namespace %s
{
	[Config((int)(AppType.ClientH))]
	public partial class TConfigValueAVOCategory : ACategory<TConfigValueAVO>
	{
	}
	
	public class TConfigValueAVO: IConfig 
	{ 
		/// <summary>标识</summary>
		public int Id { get; set; }

		/// <summary>key</summary>
		public string Name;
		
		/// <summary>描述</summary>
		public string Desc;
		
	}
	
}`
	} else {
		tpl = `using ETModel;

namespace %s
{
	[Config((int)(AppType.ClientH))]
	public partial class TSystemLanguageAVOCategory : ACategory<TSystemLanguageAVO>
	{
	}
	
	public class TSystemLanguageAVO: IConfig 
	{ 
		/// <summary>标识</summary>
		public int Id { get; set; }

		/// <summary>define</summary>
		public string Name;
		
		/// <summary>值</summary>
		public string Value;

		/// <summary>valueType</summary>
		public string ValueType;

		/// <summary>rowData</summary>
		public string RowData;

		/// <summary>描述</summary>
		public string Desc;
		
	}
		
}`
	}

	var path [2]string = [2]string{"Hotfix", "Main"}
	for _, p := range path {
		outFilename := fmt.Sprintf("%s\\%s.cs", clientCodeOutputPath+p, "T"+fileName+"AVO")
		if p == "Main" {
			tpl = fmt.Sprintf(tpl, "ETModel")
		} else if p == "Hotfix" {
			tpl = fmt.Sprintf(tpl, "ETHotfix")
		}

		outFile, err := os.Create(outFilename)
		defer outFile.Close()
		if err != nil {
			return err.Error()
		}

		_, err = outFile.Write([]byte(tpl))
		if err != nil {
			return err.Error()
		}
	}
	return ""

}
